#include "../inc/segmenttreelist.h"
#include "../inc/boundingbox.h"

SegmentTreeList::SegmentTreeList()
{
	nodes.clear();
}

SegmentTreeList::SegmentTreeList(const SegmentTreeList &_list)
{
	nodes = _list.nodes;
}

SegmentTreeList::~SegmentTreeList()
{
	nodes.clear();
}

SegmentTreeList& SegmentTreeList::operator = ( const SegmentTreeList& _list )
{
	nodes.clear();
	nodes = _list.nodes;
	
	return *this;
}

SegmentTreeList& SegmentTreeList::operator + ( const SegmentTreeList& _list )
{
	/* Because of the const parameter. <== Could create the right const methods and the methods that take const parameters. */
	list<SegmentTree*> tmp = _list.nodes;
	
	for ( list<SegmentTree*>::iterator it = tmp.begin() ; it != tmp.end() ; it++ )
		if ( !contains( (*it) ) )
			addNode( (*it) );
	
	return *this;
}

SegmentTreeList& SegmentTreeList::operator += ( const SegmentTreeList& _list )
{
	/* Because of the const parameter. <== Could create the right const methods and the methods that take const parameters. */
	list<SegmentTree*> tmp = _list.nodes;
	
	for ( list<SegmentTree*>::iterator it = tmp.begin() ; it != tmp.end() ; it++ )
		if ( !contains( (*it) ) )
			addNode( (*it) );
	
	return *this;
}

list<SegmentTree*> SegmentTreeList::getNodes()
{
	return nodes;
}

list<SegmentTree*>::iterator SegmentTreeList::getBegin()
{
	return nodes.begin();
}

list<SegmentTree*>::iterator SegmentTreeList::getEnd()
{
	return nodes.end();
}

SegmentTree* SegmentTreeList::getFirst()
{
	list<SegmentTree*>::iterator rslt = this->getBegin();
	return *rslt;
}

void SegmentTreeList::setNodes(list<SegmentTree *> _nodes)
{
	nodes = _nodes;
}

void SegmentTreeList::clear()
{
	nodes.clear();
}

bool SegmentTreeList::empty()
{
	return nodes.empty();
}

bool SegmentTreeList::contains(SegmentTree *_node)
{
	for ( list<SegmentTree*>::iterator it = nodes.begin() ; it != nodes.end() ; it++ )
	{
		if ( (*it) == _node )
			return true;
	}
	
	return false;
}

void SegmentTreeList::addNode(SegmentTree *_node)
{
	nodes.push_back( _node );
}

void SegmentTreeList::getMatchBy_Intersection(SegmentTree *_reference, SegmentTree *_target )
{
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		if ( _reference->getSegment()->intersects( (it)->getSegment() ) )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_Distance(SegmentTree* _reference, SegmentTree* _target, Mesh* _mesh, int _distMax)
{
	/* This method launches a breadth first search from every boundary vertex of the calling node to every neighbour that is less far than _distMax.
	   We then add in a node list all the segments we have found during the BFS
	   Again, for more explanations about the BFS, see :
	   http://en.wikipedia.org/wiki/Breadth-first_search
	*/
	int*			mark = (int*) malloc ( _mesh->getNVerts() * sizeof ( int ));
	list<Vertex*>	liste;

	/* Mar is a mark array : initialized to -1, then set, for each vertex to the distance to the the boundary of the segment. */
	for ( int i = 0 ; i < _mesh->getNVerts() ; i++ )
		mark[i] = -1;
	
	/* We initialize the list list with the node's segment boundary. And mark these vertices with a distance equalo to 0. */
	liste = _reference->getSegment()->getBoundary( mark );
			
	while ( !liste.empty() )
	{
		/* While list is not empty. "Defiler la liste list". */
		Vertex* v = liste.front();
		liste.pop_front();
		
		/* If the vertex is not far away from the node's segment. */
		if ( mark[ v->getID() ] <= _distMax )
		{
			/* We take a look at its neighbours. */
			for ( int i = 0 ; i < (int)v->getEdges().size() ; i++ )
			{
				Vertex* neighbour = v->getIEdge(i)->getHead();
				
				if ( mark [ neighbour->getID() ] == -1 )
				{
					/* We mark the vertex with its distance to the node's segment : the distance to v + 1 because it is v's neighbour. */
					mark[ neighbour->getID() ] = mark[ v->getID() ] + 1;
					
					/* We add the neighbour vertex to the list. */
					liste.push_back( neighbour );
					
					/* We search if this vertex is a part of a target's tree ancester.
					   If yes, then the ancester has to be added to the rslt list.
					*/
					for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
					{
						/* If the target segment contains the current vertex AND is not already in the rslt list : this avoid adding it several times in the rslt list. */
						if ( (*it).getSegment()->contains( v->getID() ) && !contains( (it) ) && it != _reference )
						{
							addNode( (it) );
						}
					}
				}
			}
		}
	}
	
	/* Don't forget to free the allocated memory. */
	free ( mark );
}

void SegmentTreeList::getMatchBy_Depth(SegmentTree *_reference, SegmentTree *_target, int _distMax)
{
	/* I am looking for sulcis so I take the maxDepth. */
	double myMaxDepth = _reference->getMaxDepth();
	double myMinDepth = _reference->getMinDepth();
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		float dist1 = myMaxDepth - (it)->getMaxDepth();
		float dist2 = myMinDepth - (it)->getMinDepth();
		
		if ( dist1 < 0 )
			dist1 *= -1;
		
		if ( dist2 < 0 )
			dist2 *= -1;
		
		if ( dist1 < _distMax && dist2 < _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_Areas(SegmentTree *_reference, Mesh* _refMesh, double _refMeshArea, SegmentTree *_target, Mesh* _targetMesh, double _targetMeshArea, double _distMax)
{
	double myArea = _reference->getSegment()->getTriangulatedAreaConverted( _refMesh, _refMeshArea );
	
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		double dist = myArea - (it)->getSegment()->getTriangulatedAreaConverted( _targetMesh, _targetMeshArea );
		
		if ( dist < 0 )
			dist *= -1;
		
		if ( dist < _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxBotX(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance = sourceBBox.getBot().getX() - targetBBox.getBot().getX();
		
		if ( distance < 0 )
			distance *= -1;
		
		if ( distance <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxTopX(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance = sourceBBox.getTop().getX() - targetBBox.getTop().getX();
		
		if ( distance < 0 )
			distance *= -1;
		
		if ( distance <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxBotY(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance = sourceBBox.getBot().getY() - targetBBox.getBot().getY();
		
		if ( distance < 0 )
			distance *= -1;
		
		if ( distance <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxTopY(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance = sourceBBox.getTop().getY() - targetBBox.getTop().getY();
		
		if ( distance < 0 )
			distance *= -1;
		
		if ( distance <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxBotZ(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance = sourceBBox.getBot().getZ() - targetBBox.getBot().getZ();
		
		if ( distance < 0 )
			distance *= -1;
		
		if ( distance <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxTopZ(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance = sourceBBox.getTop().getZ() - targetBBox.getTop().getZ();
		
		if ( distance < 0 )
			distance *= -1;
		
		if ( distance <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxX(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance1 = sourceBBox.getTop().getX() - targetBBox.getTop().getX();
		float distance2 = sourceBBox.getBot().getX() - targetBBox.getBot().getX();
		
		if ( distance1 < 0 )
			distance1 *= -1;
		
		if ( distance2 < 0 )
			distance2 *= -1;
		
		if ( distance1 <= _distMax && distance2 <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxY(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance1 = sourceBBox.getTop().getY() - targetBBox.getTop().getY();
		float distance2 = sourceBBox.getBot().getY() - targetBBox.getBot().getY();
		
		if ( distance1 < 0 )
			distance1 *= -1;
		
		if ( distance2 < 0 )
			distance2 *= -1;
		
		if ( distance1 <= _distMax && distance2 <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::getMatchBy_BoundingBoxZ(SegmentTree *_reference, BoundingBox _refMeshBBox, SegmentTree *_target, BoundingBox _targetMeshBBox, int _distMax)
{
	BoundingBox sourceBBox;
	sourceBBox.create( _reference->getSegment() );
	
	/* Let's convert the BBox of the current node's segment into a per centage of the mesh BBox. */
	sourceBBox.convertsTo( _refMeshBBox );
	
	
	/* Let's look if the node calling the methods has some matches in the _targets nodes. */
	for ( SegmentTree* it = _target ; it != NULL ; it = it->getBrother() )
	{
		BoundingBox targetBBox;
		targetBBox.create( it->getSegment() );
		
		/* Let's convert the BBox of the target node's segment into a per centage of the mesh BBox. */
		targetBBox.convertsTo( _targetMeshBBox );
		
		float distance1 = sourceBBox.getTop().getZ() - targetBBox.getTop().getZ();
		float distance2 = sourceBBox.getBot().getZ() - targetBBox.getBot().getZ();
		
		if ( distance1 < 0 )
			distance1 *= -1;
		
		if ( distance2 < 0 )
			distance2 *= -1;
		
		if ( distance1 <= _distMax && distance2 <= _distMax )
			addNode( it );
	}
}

void SegmentTreeList::display(int _mode, double _r, double _g, double _b)
{
	if ( _mode == MESH_COLOR )
	{
		for ( list<SegmentTree*>::iterator it = nodes.begin() ; it != nodes.end() ; it++ )
		{
			(*it)->getSegment()->display( FACES );
		}
	}
	
	if ( _mode == OTHER_COLOR )
	{
		for ( list<SegmentTree*>::iterator it = nodes.begin() ; it != nodes.end() ; it++ )
		{
			(*it)->getSegment()->display( FACES, _r, _g, _b );
		}
	}
	
	if ( _mode == OWN_COLOR )
	{
		for ( list<SegmentTree*>::iterator it = nodes.begin() ; it != nodes.end() ; it++ )
		{
			Vector3D color = (*it)->getColor();
			(*it)->getSegment()->display( FACES, color.getX(), color.getY(), color.getZ() );
		}
	}
}
